<?php

    /**
     * CSV
     *
     * This is a CSV parser
     *
     * @package      Core
     * @subpackage   Parser
     *
     * @license      Open Software License (OSL 3.0) see LICENCE-OSL file or http://opensource.org/licenses/osl-3.0.php
     */
	class Core_Parser_Csv
	{

    ////////////////////////////////////////////////////////////////////////////
	//                               Constantes                               //
	////////////////////////////////////////////////////////////////////////////

        /**
         * Size of the buffer
         *
         * @const integer
         */
        const BUFFER = 4096;

	////////////////////////////////////////////////////////////////////////////
	//                                Variables                               //
	////////////////////////////////////////////////////////////////////////////

        /**
         * The field enclosure character
         *
         * @var char
         */
        protected $_encloser = '"';

        /**
         * The field delimiter
         *
         * @var char
         */
        protected $_delimiter = ';';

	////////////////////////////////////////////////////////////////////////////
	//                            Getters / Setters                           //
	////////////////////////////////////////////////////////////////////////////

		/**
		 * Sets the field delimiter
		 *
		 * @param		char		$value : the new delimiter character
		 * @return		Core_Parser_Csv
		 */
		public function setDelimiter($value)
		{
			$this->_delimiter = $value;
			return $this;
		}

		/**
		 * Sets field enclosure character
		 *
		 * @param		char		$value : the new encloser character
		 * @return		Core_Parser_Csv
		 */
		public function setEncloser($value)
		{
			$this->_encloser = $value;
			return $this;
		}

	////////////////////////////////////////////////////////////////////////////
	//                                 Methods                                //
	////////////////////////////////////////////////////////////////////////////

		/**
		 * Constructor
		 */
		public function __construct()
		{
		}

		/**
		 * Destructor
		 */
		public function __destruct()
		{
			unset($this);
		}

		/**
		 * Parses a CSV file
		 *
		 * @param		string		$filename : the file to parse
		 * @throws		Core_IO_Exception
		 * @throws		Core_ArgumentException
		 * @throws		Core_ArgumentNullException
		 * @throws		Core_IO_FileNotFoundException
		 * @return		array		an array containing all the read lines
		 */
		public function read($filename)
		{
            if ($filename === null) {
				throw new Core_ArgumentNullException('filename is a null reference');
			}

            if (strlen(trim($filename)) == 0) {
				throw new Core_ArgumentException('filename is a zero-length string');
			}

			if (!Core_IO_File::exists($filename)) {
				throw new Core_IO_FileNotFoundException('Unable to find file ('. $filename .').');
			}

			$data = array();
			$fs = Core_IO_File::openRead($filename);
			if ($fs instanceof Core_IO_FileStream) {
				if ($this->_delimiter == '\\t') {
					$this->_delimiter = "\t";
				}

				while ($rowData = @fgetcsv($fs->getResource(), self::BUFFER, $this->_delimiter, $this->_encloser)) {
				    $data[] = $rowData;
				}

				$fs->close();
			}

			return $data;
		}

		/**
		 * Parses a CSV file
		 *
		 * @param		string		$filename : the file to write to
		 * @param		array		$data : to content to write to the file
		 * @param		boolean     $append : true if data append to existing file
		 * @throws		Core_IO_Exception
		 * @throws		Core_ArgumentException
		 * @throws		Core_ArgumentNullException
		 * @return		void
		 */
		public function write($filename, array $data = array(), $append = false)
		{
            if ($filename === null) {
				throw new Core_ArgumentNullException('filename is a null reference');
			}

            if (strlen(trim($filename)) == 0) {
				throw new Core_ArgumentException('filename is a zero-length string');
			}

            if ($append) {
                $fs = Core_IO_File::appendText($filename);
            } else {
                $fs = Core_IO_File::openWrite($filename);
            }
            
			if ($fs instanceof Core_IO_FileStream) {
				$this->_fputcsv(&$fs, $data);
				$fs->close();
			}

			return $data;
		}

	////////////////////////////////////////////////////////////////////////////
	//                            Protected methods                           //
	////////////////////////////////////////////////////////////////////////////

		/**
		 * Writes a CSV formatted line
		 *
		 * @param		Core_IO_FileStream		$fs
		 * @param		array					$fields
		 * @return		void
		 */
		protected function _fputcsv(Core_IO_FileStream &$fs, array $fields = array())
		{
			$str = '';
			$escape_char = '\\';

			foreach ($fields as $value) {
				if (strpos($value, $this->_delimiter) !== false ||
					strpos($value, $this->_encloser) !== false ||
					strpos($value, "\n") !== false ||
					strpos($value, "\r") !== false ||
					strpos($value, "\t") !== false ||
					strpos($value, ' ') !== false
				) {
					$escaped = 0;
					$str2 = $this->_encloser;
					$len = strlen($value);

					for ($i = 0; $i < $len; $i++) {
						if ($value[$i] == $escape_char) {
							$escaped = 1;
						} else if (!$escaped && $value[$i] == $this->_encloser) {
							$str2 .= $this->_encloser;
						} else {
							$escaped = 0;
						}

						$str2 .= $value[$i];
					}

					$str2 .= $this->_encloser;
					$str  .= $str2 . $this->_delimiter;
				} else {
					$str .= $this->_encloser . $value . $this->_encloser . $this->_delimiter;
				}
			}

			$str  = substr($str, 0, -1);
			$str .= "\n";

			$fs->write($str);
		}
	}
